/*
 * Client API
 *
 * Note: always do initClient() first !
 */
package org.dbdir.api.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import org.dbdir.core.Logger;
import org.dbdir.client.RespondHandler;
import org.dbdir.connections.TCPSocket;
import org.dbdir.core.helper.ACL;
import org.dbdir.core.helper.Database;
import org.dbdir.core.xml.XMLParser;
import org.w3c.dom.NodeList;

/**
 *
 * @author thotheolh
 */
public class Client {

    private TCPSocket sock = null;
    private BufferedReader in = null;
    private String fromServer;
    private String fromUser;
    private String response;
    private String username = null;
    private RespondHandler handler;
    //private String currentDBProfile = ""; //current db profile
    private String accStr = ""; //accessory used string
    private boolean isConnected = false;
    private boolean isLogin = false;
    private Logger logger = new Logger();

    public Client() {
        handler = new RespondHandler();
        String ipaddr = getIPAddrFromConf();
        sock = new TCPSocket(null, ipaddr, 10999);
        if (sock.isConnected()) {
            in = sock.getIn();
            initConn();
        }
    }

    public void doLogin(String username, String password) {
        if (isConnected()) {
            try {
                getSock().output("login usr=" + username + " pwd=" + password);
                setResponse(getIn().readLine());
                if (getResponse().equals("auth-ok")) {
//                    System.out.println("auth-ok");
                    this.setUsername(username);
                    setIsLogin(true);
                } else if (getResponse().equals("auth-fail")) {
//                    System.out.println("auth-fail");
                    setIsLogin(false);
                } else if (getResponse().equals("auth-bad-cmd")) {
//                    System.out.println("auth-bad-cmd");
                    setIsLogin(false);
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
            }
        }
    }

    public boolean isAdmin(String dbname) {
        try {//TODO:Continue... check if is admin.
            sock.output("isadm dbalias=" + dbname);
            response = in.readLine();
            if (response.equals("isadm-true")) {
                return true;
            } else {
                return false;
            }
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return false;
        }
    }

    public boolean joindb(String dbalias) {
        try {
            sock.output("joindb dbalias=" + dbalias);
            if (in.readLine().equals("joindb-ok")) {
                return true;
            } else {
                return false;
            }
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return false;
        }
    }

    public boolean addUser(String username, String password) {
        try {
            sock.output("addusr usr=" + username + " pwd=" + password);
            if (in.readLine().equals("addusr-ok")) {
                return true;
            } else {
                return false;
            }
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return false;
        }
    }

    public boolean modUserPwd(String oldPwd, String newPwd) {
        if (isConnected() && isLogin) {
            try {
                sock.output("modme" + " oldpwd=" + oldPwd + " newpwd=" + newPwd);
                response = in.readLine();
                if (response.equals("modme-ok")) {
                    return true;
                } else if (response.equals("modme-fail")) {
                    return false;
                } else {
                    return false;
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return false;
            }
        } else {
            return false;
        }
    }

    public boolean makeUser(String username, String password) { //can be used for registration
        if (isConnected()) {
            try {
                sock.output("addusr usr=" + username + " pwd=" + password);
                if (in.readLine().equals("addusr-ok")) {
                    return true;
                } else {
                    return false;
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return false;
            }
        } else {
            return false;
        }
    }

    public boolean removeMe() { //Delete yourself
        try {
            sock.output("rmme");
            response = in.readLine();
            if (response.equals("rmme-ok")) {
                return true;
            } else {
                return false;
            }
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return false;
        }
    }

    /* Remove user from a database profile group.
     * Must be admin to use this ability.
     */
    public boolean removeUser(String username, String dbalias) {
        if (isConnected() && isLogin) {
            try {
                sock.output("rmusr dbalias=" + dbalias + " usr=" + username);
                if (in.readLine().equals("rmusr-ok")) {
                    return true;
                } else {
                    return false;
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return false;
            }
        }
        return false;
    }

    public boolean doUpdatePersonal(String oldpass, String newpass) { // Update user personal info
        if (isConnected() && isLogin) {
            try {
                sock.output("modme" + " oldpwd=" + oldpass + " newpwd=" + newpass);
                response = in.readLine();
                if (response.equals("modme-ok")) {
                    return true;
                } else if (response.equals("modme-fail")) {
                    return false;
                } else {
                    return false;
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return false;
            }
        }
        return false;
    }

    /**
     * Gets profile of a database that's registered.
     * @return
     */
    public Database getDbProfile(String dbname) {
        if (isConnected() && isLogin) {
            try {
                sock.output("getdb dbalias=" + dbname);
                response = in.readLine();
                return getHandler().getDB(response);
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return null;
            }
        }
        return null;
    }

    public boolean createDbProfile(Database db) {
        if (isConnected() && isLogin) {
            try {
                sock.output("mkdb dbalias=" + db.getDbAlias() + " dbpath=" + db.getDbPath() + " dbusr=" + db.getDbUsername() + " dbpwd=" + db.getDbPassword());
                response = in.readLine();
                if (response.equals("mkdb-ok")) {
                    return true;
                } else {
                    return false;
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return false;
            }
        }
        return false;
    }

    public boolean deleteDbProfile(String dbname) {
        if (isConnected() && isLogin) {
            try {
                sock.output("deldb dbalias=" + dbname);
                if (in.readLine().equals("deldb-ok")) {
                    return true;
                } else {
                    return false;
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return false;
            }
        }
        return false;
    }

    public ArrayList getUserDBList() {
        sock.output("getdbls");
        try {
            return getHandler().convertStringToArray(in.readLine(), "getdblsrs");
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return null;
        }
    }

    public ArrayList getAvDBList() {
        try {
            sock.output("getavdbls");
            return getHandler().convertStringToArray(in.readLine(), "getavdblsrs");
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return null;
        }
    }

    public ACL getACL(String username, String dbname) {
        if (isConnected() && isLogin) {
            try {
                sock.output("getacl usr=" + username + " dbalias=" + dbname);
                response = in.readLine();
                return getHandler().getACL(response);
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return null;
            }
        }
        return null;
    }

    public boolean updateACL(ACL acl) {
        if (isConnected() && isLogin) {
            try {
                sock.output("modacl usr=" + acl.getUsername() + " dbalias=" + acl.getDbAlias() + " sdbpath=" + acl.isShowDbPath() + " sdbusr=" + acl.isShowDbUsername() + " sdbpwd=" + acl.isShowDbPassword() + " sdtreg=" + acl.isShowDateTimeReg() + " edbpath=" + acl.isEditDbPath() + " edbusr=" + acl.isEditDbUsername() + " edbpwd=" + acl.isEditDbPassword());
                response = in.readLine();
                if (response.equals("modacl-ok")) {
                    return true;
                } else if (response.equals("modacl-deny-mod-admin")) {
                    this.setAccStr("err-modacl-deny-mod-admin");
                    return false;
                } else {
                    return false;
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                return false;
            }
        }
        return false;
    }

    public ArrayList getDBUserList(String currentDBProfile) {
        sock.output("getusrls dbalias=" + currentDBProfile);
        try {
            return getHandler().convertStringToArray(in.readLine(), "getusrlsrs");
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return null;
        }
    }

    public boolean mkDBProfile(Database db) {
        try {
            sock.output("mkdb dbalias=" + db.getDbAlias() + " dbpath=" + db.getDbPath() + " dbusr=" + db.getDbUsername() + " dbpwd=" + db.getDbPassword());
            response = in.readLine();
            if (response.equals("mkdb-ok")) {
                return true;
            } else {
                return false;
            }
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return false;
        }
    }

    public boolean updateDBProfile(Database db) {
        try {
            sock.output("moddb dbalias=" + db.getDbAlias() + " dbpath=" + db.getDbPath() + " dbusr=" + db.getDbUsername() + " dbpwd=" + db.getDbPassword());
            response = in.readLine();
            if (response.equals("moddb-ok")) {
                return true;
            } else {
                return false;
            }
        } catch (IOException ex) {
            logger.log(2, ex.getMessage());
            return false;
        }
    }

    public boolean endSession() { //perform during logout.
        boolean status = false;
        if (isConnected()) {
            try {
                sock.output("end");
                if (in.readLine().equals("ended")) {
                    sock.close();
                    in.close();
                    this.setIsConnected(true);
                } else {
                    sock.close();
                    in.close();
                    this.setIsConnected(true);
                }
            } catch (IOException ex) {
                logger.log(2, ex.getMessage());
                this.setIsConnected(false);
            } finally {
                if (!isConnected()) {
                    status = true;
                } else {
                    status = false;
                }
            }
        }
        return status;
    }

    private String getIPAddrFromConf() {
        XMLParser xml = new XMLParser();
        NodeList nodes = xml.doParse("client.xml", "ipaddr");
        return nodes.item(0).getTextContent();
    }

    public void initConn() {
        try {
            getSock().output("dbdir");
            if (getIn().readLine().equals("ack")) {
                setIsConnected(true);
//                System.out.println("System is ready !");
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * @return the sock
     */
    public TCPSocket getSock() {
        return sock;
    }

    /**
     * @param sock the sock to set
     */
    public void setSock(TCPSocket sock) {
        this.sock = sock;
    }

    /**
     * @return the in
     */
    public BufferedReader getIn() {
        return in;
    }

    /**
     * @param in the in to set
     */
    public void setIn(BufferedReader in) {
        this.in = in;
    }

    /**
     * @return the fromServer
     */
    public String getFromServer() {
        return fromServer;
    }

    /**
     * @param fromServer the fromServer to set
     */
    public void setFromServer(String fromServer) {
        this.fromServer = fromServer;
    }

    /**
     * @return the fromUser
     */
    public String getFromUser() {
        return fromUser;
    }

    /**
     * @param fromUser the fromUser to set
     */
    public void setFromUser(String fromUser) {
        this.fromUser = fromUser;
    }

    /**
     * @return the response
     */
    public String getResponse() {
        return response;
    }

    /**
     * @param response the response to set
     */
    public void setResponse(String response) {
        this.response = response;
    }

    /**
     * @return the username
     */
    public String getUsername() {
        return username;
    }

    /**
     * @param username the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @return the handler
     */
    public RespondHandler getHandler() {
        return handler;
    }

    /**
     * @param handler the handler to set
     */
    public void setHandler(RespondHandler handler) {
        this.handler = handler;
    }

    /**
     * @return the accStr
     */
    public String getAccStr() {
        return accStr;
    }

    /**
     * @param accStr the accStr to set
     */
    public void setAccStr(String accStr) {
        this.accStr = accStr;
    }

    /**
     * @return the isConnected
     */
    public boolean isConnected() {
        return isConnected;
    }

    /**
     * @param isConnected the isConnected to set
     */
    public void setIsConnected(boolean isConnected) {
        this.isConnected = isConnected;
    }

    /**
     * @return the isLogin
     */
    public boolean isIsLogin() {
        return isLogin;
    }

    /**
     * @param isLogin the isLogin to set
     */
    public void setIsLogin(boolean isLogin) {
        this.isLogin = isLogin;
    }
}
